#!/bin/sh

# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) 2010-2011 Roman Weber (roman@openelec.tv)
# Copyright (C) 2012 Yann Cézard (eesprit@free.fr)
# Copyright (C) 2009-2014 Stephan Raue (stephan@openelec.tv)
# Copyright (C) 2016-2018 Team LibreELEC (https://libreelec.tv)
# Copyright (C) 2018-present Team CoreELEC (https://coreelec.org)

# create directories
/usr/bin/busybox mkdir -p /dev
/usr/bin/busybox mkdir -p /proc
/usr/bin/busybox mkdir -p /sys
/usr/bin/busybox mkdir -p /tmp
/usr/bin/busybox mkdir -p /flash
/usr/bin/busybox mkdir -p /sysroot
/usr/bin/busybox mkdir -p /storage

# temp mountpoint for updates
/usr/bin/busybox mkdir -p /update

# mount all needed special filesystems
/usr/bin/busybox mount -t devtmpfs devtmpfs /dev
/usr/bin/busybox mount -t proc proc /proc
/usr/bin/busybox mount -t sysfs sysfs /sys

# set needed variables
MODULE_DIR=/usr/lib/modules

UPDATE_ROOT=/storage/.update
UPDATE_DIR="$UPDATE_ROOT"

UPDATE_KERNEL="KERNEL"
UPDATE_SYSTEM="SYSTEM"
IMAGE_KERNEL="@KERNEL_NAME@"
IMAGE_SYSTEM="SYSTEM"

BOOT_STEP="start"
MD5_FAILED="0"
RUN_FSCK="yes"
RUN_FSCK_DISKS=""
SYSLINUX_DEFAULT=""
GRUB_DEFAULT=""

NBD_DEVS="0"
FLASH_FREE_MIN="5"

LIVE="no"

BREAK_TRIPPED="no"

BIGFONT="1080"

TEE_PID=""

DEVICE=$(tr -d '\0' < /sys/firmware/devicetree/base/model)

# Get a serial number if present (eg. RPi) otherwise use MAC address from eth0
MACHINE_UID="$(awk '/^Serial/{s='0000000' $3; print substr(s, length(s) - 7)}' /proc/cpuinfo)"
[ -z "$MACHINE_UID" ] && MACHINE_UID="$(cat /sys/class/net/eth0/address 2>/dev/null | tr -d :)"

# common functions
. /functions

if [ "$DEVICE" == "Anbernic RG552" ]; then
  # Enable WIFI GPIO for WIFI manipulation
  echo 113 > /sys/class/gpio/export

  # Power up the WIFI device
  echo out > /sys/class/gpio/gpio113/direction
  echo 1 > /sys/class/gpio/gpio113/value
elif [ "$DEVICE" == "Anbernic RG351P" ]; then
  # Enable GPIO 77 for power LED manipulation
  echo 77 > /sys/class/gpio/export

  # Enable WIFI GPIO for WIFI manipulation
  echo 110 > /sys/class/gpio/export

  # Power up the WIFI device
  echo out > /sys/class/gpio/gpio110/direction
  echo 1 >  /sys/class/gpio/gpio110/value

  # Enable PWM for rumble and turn rumble off during startup.
  echo 0 > /sys/class/pwm/pwmchip0/export
  echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
  echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
  echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
else #RG351V and RG351MP
  # Enable GPIO 77 for power LED manipulation
  echo 77 > /sys/class/gpio/export

  # Enable WIFI GPIO for WIFI manipulation
  echo 5 > /sys/class/gpio/export

  # Power up the WIFI device
  echo out > /sys/class/gpio/gpio5/direction
  echo 1 >  /sys/class/gpio/gpio5/value

  if [ ! "$DEVICE" == "Anbernic RG351MP" ]; then
    # Enable PWM for rumble and turn rumble off during startup.
    echo 0 > /sys/class/pwm/pwmchip0/export
    echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
    echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
    echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
  fi
fi

# script functions
progress() {
  if test "$PROGRESS" = "yes"; then
    echo "### $1 ###" >&2
  fi
}

debug_msg() {
  echo "$1" >&$SILENT_OUT
}

debug_shell() {
  redirect_output_to_screen  # restore output to a screen
  echo "### Starting debugging shell for boot step: $BOOT_STEP... type  exit  to quit ###"
  showcursor
  setsid cttyhack sh
}

error() {
  # Display fatal error message
  # $1:action which caused error, $2:message
  # Send debug_shell output to stderr, in case caller is redirecting/consuming stdout
  # Return exitcode=1 so that called may detect when an error has occurred
  echo "*** Error in $BOOT_STEP: $1: $2 ***" >&2
  debug_shell >&2
  return 1
}

break_after() {
  # Start debug shell after boot step $1, and all subsequent steps
  if [ $BREAK_TRIPPED == yes ]; then
    debug_shell
  else
    case $BREAK in
      all|*$1*)
        BREAK_TRIPPED=yes
        debug_shell
        ;;
    esac
  fi
}

# Mount handlers
# All handlers take the following parameters:
# $1:target, $2:mountpoint, $3:mount options, [$4:fs type]
mount_common() {
  # Common mount handler, handles block devices and filesystem images
  MOUNT_OPTIONS="-o $3"
  [ -n "$4" ] && MOUNT_OPTIONS="-t $4 $MOUNT_OPTIONS"

  for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
    ERR_ENV=1

    mount $MOUNT_OPTIONS $1 $2 >&$SILENT_OUT 2>&1
    [ "$?" -eq "0" ] && ERR_ENV=0 && break

    usleep 1000000
  done
  [ "$ERR_ENV" -eq "0" ] && return 0
  error "mount_common" "Could not mount $1"
}

get_iscsistart_options() {
  # Convert kernel commandline ISCSI= options to iscsistart options
  IFS_SAVE="$IFS"
  IFS=,

  for arg in $1; do
    val="${arg#*=}"
    case "$arg" in
      iscsi_initiator=*)
        option="-i"
        ;;
      iscsi_target_name=*)
        option="-t"
        ;;
      iscsi_target_ip=*)
        option="-a"
        ;;
      iscsi_target_port=*)
        option="-p"
        ;;
      iscsi_target_group=*)
        option="-g"
        ;;
      iscsi_username=*)
        option="-u"
        ;;
      iscsi_password=*)
        option="-w"
        ;;
      iscsi_in_username=*)
        option="-U"
        ;;
      iscsi_in_password=*)
        option="-W"
        ;;
    esac
    echo "$option $val"
  done

  IFS="$IFS_SAVE"
}

mount_iscsi() {
  # Mount iSCSI target
  ISCSI_DEV="${1##*,}"
  ISCSI_OPTIONS="${1%,*}"

  if [ ! -f "/usr/sbin/iscsistart" ]; then
    error "iscsistart" "iSCSI support not available"
  fi

  if [ "$ISCSI_OPTIONS" = "auto" ]; then
    progress "Network configuration based on iBFT"
    /usr/sbin/iscsistart -N >&$SILENT_OUT 2>&1 || error "iscsistart" "Unable to configure network"
    progress "iSCSI auto connect based on iBFT"
    /usr/sbin/iscsistart -b >&$SILENT_OUT 2>&1 || error "iscsistart" "Unable to auto connect"
  else
    /usr/sbin/iscsistart $(get_iscsistart_options "$ISCSI_OPTIONS") >&$SILENT_OUT 2>&1 || error "iscsistart" "Unable to connect to ISCSI target"
  fi

  mount_common "$ISCSI_DEV" "$2" "$3" "$4"
}

mount_nbd() {
# Mount NBD device
  NBD_SERVER="${1%%:*}"
  NBD_PORT="${1#*:}"
  NBD_DEV="/dev/nbd$NBD_DEVS"

  nbd-client $NBD_SERVER $NBD_PORT $NBD_DEV >&$SILENT_OUT 2>&1 || error "nbd-client" "Could not connect to NBD server $1"

  mount_common "$NBD_DEV" "$2" "$3" "$4"

  NBD_DEVS=$(( NBD_DEVS + 1 ))
}

mount_nfs() {
  # Mount NFS export
  NFS_EXPORT="${1%%,*}"
  NFS_OPTIONS="${1#*,}"

  [ "$NFS_OPTIONS" = "$1" ] && NFS_OPTIONS=

  mount_common "$NFS_EXPORT" "$2" "$3,nolock,rsize=32768,wsize=32768,$NFS_OPTIONS" "nfs"
}

mount_ubifs() {
  mount_common "$1" "$2" "$3" "ubifs"
}

#  mount_folder "$boot" "/flash" "ro,noatime"
# $1:[TYPE=]target, $2:mountpoint, $3:mount options, [$4:fs type]
mount_folder() {
  local target="${1#*=}"

  mkdir -p /dev/bind_tmp
  mount_common "$target" "/dev/bind_tmp" "rw,noatime"
  mount_common "/dev/bind_tmp/coreelec_$(basename $2)" "$2" "bind"
  umount /dev/bind_tmp &>/dev/null

  [ "$2" = "/flash" ] && mount -o remount,ro /flash
  [ -z "$(ls -A /dev/bind_tmp)" ] && rm -rf /dev/bind_tmp
}

mount_part() {
  # Mount a local or network filesystem
  # $1:[TYPE=]target, $2:mountpoint, $3:mount options, [$4:fs type]
  progress "mount filesystem $1 ..."

  MOUNT_TARGET="${1#*=}"
  case $1 in
    /dev/ubi*)
      MOUNT_CMD="mount_ubifs"
      MOUNT_TARGET="$1"
      RUN_FSCK="no"
      ;;
    LABEL=*|UUID=*|/*)
      MOUNT_CMD="mount_common"
      MOUNT_TARGET="$1"
      ;;
    ISCSI=*)
      MOUNT_CMD="mount_iscsi"
      ;;
    NBD=*)
      MOUNT_CMD="mount_nbd"
      ;;
    NFS=*)
      MOUNT_CMD="mount_nfs"
      ;;
    FOLDER=*)
      MOUNT_CMD="mount_folder"
      MOUNT_TARGET="$1"
      ;;
    *)
      error "mount_part" "Unknown filesystem $1"
      ;;
  esac

  # Substitute unique identifier if available or remove placeholder
  MOUNT_TARGET="${MOUNT_TARGET//@UID@/$MACHINE_UID}"

  $MOUNT_CMD "$MOUNT_TARGET" "$2" "$3" "$4"
}

mount_sysroot() {
  if [ "$SYSTEM_TORAM" = "yes" ]; then
    cp /flash/$IMAGE_SYSTEM /dev/$IMAGE_SYSTEM
    mount_part "/dev/$IMAGE_SYSTEM" "/sysroot" "ro,loop"
  else
    mount_part "/flash/$IMAGE_SYSTEM" "/sysroot" "ro,loop"
  fi

  if [ -f /flash/post-sysroot.sh ]; then
    . /flash/post-sysroot.sh
  fi
}

# mount the specified SYSTEM file and output arch from /etc/os-release
get_project_arch() {
  if [ -f ${1}/etc/os-release ]; then
    . ${1}/etc/os-release
    echo "${COREELEC_ARCH:-${LIBREELEC_ARCH}}"
  fi
}

# mount the specified SYSTEM file and output version from /etc/os-release
get_project_version() {
  if [ -f ${1}/etc/os-release ]; then
    . ${1}/etc/os-release
    echo "${VERSION}"
  fi
}

# If the project/arch of current matches the update, then it is considered compatible.
# Otherwise, mount the update SYSTEM partition and, if canupdate.sh is available,
# call the script to determine if the current update file can be applied on to the
# current system - 0 means it is compatible, non-zero that it is not compatible.
is_compatible() {
  local result=1

  if [ "${1}" = "${2}" ]; then
    result=0
  else
    if [ -f /update/usr/share/bootloader/canupdate.sh ]; then
      sh /update/usr/share/bootloader/canupdate.sh "${1}" "${2}" && result=0
    fi
  fi

  return ${result}
}

# determine if the new SYSTEM file is compatible with the current SYSTEM file
check_is_compatible() {
  local update_filename="${1}"
  local old_project_arch new_project_arch

  old_project_arch="$(get_project_arch "/sysroot")" || return
  new_project_arch="$(get_project_arch "/update")" || return

  # If old or new project/arch isn't available then could be very old (pre-/etc/os-release) build - have to trust it
  if [ -n "${old_project_arch}" -a -n "${new_project_arch}" ]; then
    # If the old project/arch is not compatible with the new project/arch then abort...
    if ! is_compatible "${old_project_arch}" "${new_project_arch}"; then
      echo ""
      echo "ERROR: $(basename "${update_filename}") is not compatible with ${old_project_arch} hardware - update cancelled."
      echo ""
      echo "Current system: ${old_project_arch}"
      echo "Update  system: ${new_project_arch}"
      echo ""
      echo "Create $UPDATE_ROOT/.nocompat to disable compatibility checks and risk a non-booting system."
      echo ""
      return 1
    fi
  fi

  return 0
}

display_versions() {
  local old_project_version new_project_version

  old_project_version="$(get_project_version "/sysroot")" || return
  new_project_version="$(get_project_version "/update")" || return

  if [ -n "${old_project_version}" -a -n "${new_project_version}" ]; then
    echo ""
    echo "Updating from ${old_project_version} to ${new_project_version}"
    echo ""
  fi

  return 0
}

update_file() {
  if [ -f "$UPDATE_DIR/$2" -a -f "$3" ]; then
    mount -o remount,rw /flash

    StartProgress percent "Updating $1... " "$3" $(stat -t "$UPDATE_DIR/$2" | awk '{print $2}')
      # use dd here with conv=fsync so that all writes are non-buffered
      # ensuring accurate progress - take the sync hit during the
      # transfer, rather than when flushing file buffers after the progress
      # meter declares the transfer already complete
      dd if=$UPDATE_DIR/$2 of=$3 bs=1M conv=fsync 2>/dev/null

      StopProgress

    # loopback file needs writable /flash all the time
    if [ "${disk%%=*}" != "FILE" ]; then
      mount -o remount,ro /flash
    fi
    sync
  fi
}

update_partition() {
  local result

  if [ -f "$UPDATE_DIR/$2" -a -b "$3" ]; then
    StartProgress spinner "Updating $1... "
      result="$(dd if="$UPDATE_DIR/$2" of="$3" 2>&1)"
      StopProgress "done"
      sync
    echo "${result}"
  fi
}

update_bootloader() {
  local result

  export BOOT_ROOT="/flash"
  export SYSTEM_ROOT="/update"

  if [ -f $SYSTEM_ROOT/usr/share/bootloader/update.sh ]; then
    echo ""
    echo "Updating Boot Files... "
    sh $SYSTEM_ROOT/usr/share/bootloader/update.sh
    sync
    echo "Boot Files Updated."
    echo ""
  fi
}

load_modules() {
  progress "Loading kernel modules"

  [ ! -f "/etc/modules" ] && return
  for module in $(cat /etc/modules); do
    progress "Loading kernel module $module"
    insmod "$MODULE_DIR/$module.ko" || progress "... Failed to load kernel module $module, skipping"
  done
}

set_consolefont() {
  local hres

  progress "Set console font"
  if [ -e /dev/fb0 ]; then
    hres="$(fbset 2>/dev/null | awk '/geometry/ { print $2 }')"
    vres="$(fbset 2>/dev/null | awk '/geometry/ { print $3 }')"
    if [ "$DEVICE" == "Anbernic RG351V" ] || [ "$DEVICE" == "Anbernic RG351MP" ] || [ "$DEVICE" == "PowKiddy Magicx XU10" ] || [ "$DEVICE" == "SZDiiER D007 Plus" ]; then
      setfont -C /dev/tty0 ter-v20n.psf
    elif [ "$DEVICE" == "Anbernic RG552" ]; then
      setfont -C /dev/tty0 ter-v32n.psf
    else
      setfont -C /dev/tty0 ter-v14n.psf
    fi
  fi
}

load_splash() {
  local set_default_res=no
  local vres

  if [ ! "$SPLASH" = "no" ]; then
    progress "Loading bootsplash"

    # load uvesafb module if needed
    if [ -f "$MODULE_DIR/uvesafb.ko" -a ! -e /dev/fb0 ]; then
      progress "Loading kernel module uvesafb.ko"
      insmod "$MODULE_DIR/uvesafb.ko" && set_default_res=yes || progress "... Failed to load kernel module uvesafb, skipping"
    fi

    if [ -e /dev/fb0 ]; then
      # Set framebuffer to a custom resolution and/or fallback to default resolution (1024x768-32), if required.
      if [ ! "$SWITCH_FRAMEBUFFER" = "no" ]; then
        if [ "$SWITCH_FRAMEBUFFER" = "1080" ]; then
          SWITCH_FRAMEBUFFER="1920 1080 1920 1080 32"
        elif [ "$SWITCH_FRAMEBUFFER" = "720" ]; then
          SWITCH_FRAMEBUFFER="1280 720 1280 720 32"
        fi

        # Try setting a custom framebuffer resolution
        if [ ! "${SWITCH_FRAMEBUFFER:-yes}" = "yes" ]; then
          fbset -g $SWITCH_FRAMEBUFFER 2>/dev/null && set_default_res=no
        fi

        # Set a default resolution if required
        if [ "$set_default_res" = "yes" ]; then
          fbset -g 1024 768 1024 768 32
        fi
      fi

      if [ "$DEVICE" == "Anbernic RG552" ]; then
        fbset -g 1152 1920 1152 1920 32
      fi

      # load splash
      if [ -f /splash/splash.conf ]; then
        . /splash/splash.conf
      fi

      # Select splash image based on current native resolution
      if [ -z "$SPLASHIMAGE" ]; then
        vres="$(fbset 2>/dev/null | awk '/geometry/ { print $3 }')"
        hres="$(fbset 2>/dev/null | awk '/geometry/ { print $2 }')"
        if [ "${hres}" = "640" ]
        then
          RES="${hres}"
        else
          RES="${vres}"
        fi

        for s in /splash/splash-${RES}.png \
                 /splash/splash-1080.png \
                 ; do
          if [ -f "${s}" ]; then
            SPLASHIMAGE="${s}"
            break
          fi
        done
      fi

      R_DEVICE="$(cat /storage/.config/device)" >/dev/null 2>&1
      if [ "$R_DEVICE" = "R33S" ]; then
        S_DEVICE="Game Console R33S"
      elif [ "$R_DEVICE" = "R3xS" ]; then
        S_DEVICE="Game Console R3xS/PowKiddy RGB20S"
      elif [ "$R_DEVICE" = "Unknown" ]; then
        S_DEVICE="Unknown"
      else
        S_DEVICE="$DEVICE"
      fi

      source /sysroot/etc/os-release

      ply-image /sysroot/usr/config/splash/blank.png > /dev/null 2>&1
      if [ -n "$SPLASHIMAGE" -a -f "$SPLASHIMAGE" ]; then
        ply-image $SPLASHIMAGE > /dev/null 2>&1
      fi
      echo -en "\033[10000H\033[2K  Device:  ${S_DEVICE}\n  Image:   ${COREELEC_DEVICE}\n  Version: ${VERSION} (${BUILD_ID:0:7})\n  Built:   ${BUILD_DATE}\n" >/dev/console

      debug_msg "Framebuffer vertical res: $vres"
      debug_msg "Framebuffer splash image: $SPLASHIMAGE"
    fi
  fi
}

do_reboot() {
  echo "System reboots now..."

  # stop output redirection
  [ -n "$TEE_PID" ] && kill $TEE_PID &>/dev/null
  if [ -s /dev/init.log ]; then
    mv /dev/init.log /storage/init-previous.log
  fi
  redirect_output_to_screen
  delete_descriptors

  # syncing filesystem
  sync

  # unmount filesystems
  if /usr/bin/busybox mountpoint -q /flash ; then
    /usr/bin/busybox umount /flash &>/dev/null
  fi

  if /usr/bin/busybox mountpoint -q /storage ; then
    /usr/bin/busybox umount /storage &>/dev/null
  fi

  usleep 2000000
  /usr/bin/busybox reboot
}

force_fsck() {
  echo "Filesystem corruption has been detected!"
  echo "To prevent an automatic repair attempt continuing,"
  echo "press any key or power off your system within the next 120 seconds"
  echo ""
  read -t120 -n1
  # The exit status is 0 if input is available
  # The exit status is greater than 128 if the timeout is exceeded
  if [ $? -ne 0 -o $? -gt 128 ]; then
    echo "Repairing filesystem..."
    echo ""
    /usr/sbin/fsck -T -M -y $RUN_FSCK_DISKS
    FSCK_RET=$?
    if [ $(( $FSCK_RET & 8 )) -eq 8 ]; then
      # fubar
      echo "Forced fsck failed. Your system is broken beyond repair"
      echo "Please re-install @DISTRONAME@"
      echo ""
      echo "Press enter to shutdown now"
      echo ""
      read fubar
      poweroff
    fi
    do_reboot
  else
    echo "Shutting down..."
    sleep 5
    sync
    poweroff
  fi
}

check_disks() {
  if [ "$RUN_FSCK" = "yes" -a -n "$RUN_FSCK_DISKS" ]; then
    progress "Checking disk(s): $RUN_FSCK_DISKS"
    echo "Checking disk(s): $RUN_FSCK_DISKS" >/dev/kmsg
    for i in 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0; do
      /usr/sbin/fsck -T -M -p -a $RUN_FSCK_DISKS >/dev/fsck.latest 2>&1
      FSCK_RET=$?
      cat /dev/fsck.latest >>/dev/fsck.log

      # FSCK_RET is the bit-wise OR of the exit codes for each filesystem that is checked.
      if [ $FSCK_RET -ge 16 ]; then
        progress "General error, continuing..."
        break
      elif [ $(( $FSCK_RET & 8 )) -eq 8 ]; then
        # device not found
        if [ $i -eq 0 ]; then
          progress "Device not found, continuing..."
        else
          usleep 500000
        fi
      elif [ $(( $FSCK_RET & 4 )) -eq 4 ]; then
        # errors left
        force_fsck
      elif [ $(( $FSCK_RET & 2 )) -eq 2 ]; then
        # reboot needed
        echo "Filesystem repaired, reboot needed..."
        do_reboot
      elif [ $(( $FSCK_RET & 1 )) -eq 1 ]; then
        # filesystem errors corrected
        progress "Filesystem errors corrected , continuing..."
        break
      elif [ $FSCK_RET -eq 0 ]; then
        # no errors found
        progress "No filesystem errors found, continuing..."
        break
      fi
    done
    while read line; do
      [ -n "$line" ] && echo "fsck: ${line::160}" >/dev/kmsg
    done </dev/fsck.latest
    rm -f /dev/fsck.latest
  fi
}

wakeonlan() {
  if [ "$STORAGE_NETBOOT" = "yes" ]; then
    wol_ip=${disk%:*}
    wol_ip=${wol_ip#*=}
  elif [ "$FLASH_NETBOOT" = "yes" ]; then
    wol_ip=${boot%:*}
    wol_ip=${wol_ip#*=}
  else
    return 0
  fi

  if [ -n "$wol_ip" -a -n "$wol_mac" -a -n "$wol_wait" ]; then
    progress "Sending Magic Packet (WOL) if needed"

    if ! ping -q -c 2 "$wol_ip" &>/dev/null; then
      ether-wake "$wol_mac"
      StartProgress countdown "WOL magic packet sent to $wol_ip, waiting $wol_wait seconds... " $wol_wait "done"
    fi
  fi
}

mount_flash() {
  progress "Mounting flash"

  wakeonlan

  mount_part "$boot" "/flash" "ro,noatime"

  if [ -f /flash/post-flash.sh ]; then
    . /flash/post-flash.sh
  fi
}

cleanup_flash() {
  progress "Cleaning up flash (if required)"

  if [ -f /flash/pieeprom.bin -o -f /flash/pieeprom.upd -o -f /flash/vl805.bin ]; then
    mount -o remount,rw /flash

    rm -f /flash/pieeprom.bin /flash/pieeprom.upd /flash/pieeprom.sig
    rm -f /flash/vl805.bin /flash/vl805.sig
    rm -f /flash/recovery.bin /flash/recovery.[0-9][0-9][0-9] /flash/RECOVERY.[0-9][0-9][0-9]

    mount -o remount,ro /flash
  fi
}

mount_storage() {
  progress "Mounting storage"

  if [ "$LIVE" = "yes" ]; then
    # mount tmpfs and exit early. disk=xx is not allowed in live mode
    mount -t tmpfs none /storage
    return
  fi

  wakeonlan

  if [ -n "$disk" ]; then
    if [ -n "$OVERLAY" ]; then
      OVERLAY_DIR=$(cat /sys/class/net/eth0/address | tr -d :)

      mount_part "$disk" "/storage" "rw,noatime"
      mkdir -p /storage/$OVERLAY_DIR
      umount /storage &>/dev/null

      # split $disk into $target,$options so we can append $OVERLAY_DIR
      options="${disk#*,}"
      target="${disk%%,*}"
      if [ "$options" = "$disk" ]; then
        disk="$target/$OVERLAY_DIR"
      else
        disk="$target/$OVERLAY_DIR,$options"
      fi
    fi

    if [ -f /flash/mount-storage.sh ]; then
      . /flash/mount-storage.sh
    else
      mount_part "$disk" "/storage" "rw,noatime"
    fi
  else
    # /storage should always be writable
    mount -t tmpfs none /storage
  fi
}

mount_games() {
  if /usr/bin/busybox mountpoint -q /storage ; then
    progress "Mounting games"
    if [ ! -d "/storage/roms" ]
    then
      /usr/bin/busybox mkdir -p /storage/roms >/dev/null 2>&1
    fi

    for DEV in mmcblk1p3 mmcblk1p1 mmcblk1 mmcblk0p3
    do
      if [ -e "/dev/${DEV}" ] && [ ! -e "/storage/.please_resize_me" ]
      then
	FS=$(blkid /dev/${DEV} | grep -e 'exfat' -e 'ext4' &>/dev/null)
        if [ "$?" == "0" ]
	then
          mount /dev/${DEV} /storage/roms >/dev/null 2>&1
          break
        else
	  echo "Unsupported games filesystem detected. Please reformat with exFAT or ext4."
	  echo ""
	  StartProgress countdown "Shutting down in 5... " 30 "NOW"
	  sync
	  poweroff
	fi
      fi
    done

    if [ -d "/storage/.update" ] && [ ! -e "/storage/.please_resize_me" ]
    then
      /usr/bin/busybox rm -rf /storage/.update >/dev/null 2>&1
      /usr/bin/busybox mkdir -p /storage/.update >/dev/null 2>&1
    fi

    if [ ! -d "/storage/roms/update" ]
    then
      /usr/bin/busybox mkdir -p /storage/roms/update >/dev/null 2>&1
    fi

    /usr/bin/busybox mountpoint -q /storage/roms >/dev/null 2>&1
    if [ $? == "0" ] && [ ! -e "/storage/.please_resize_me" ]
    then
      /usr/bin/busybox mkdir -p "$UPDATE_ROOT" >/dev/null 2>&1
      mount --bind /storage/roms/update "$UPDATE_ROOT" >/dev/null 2>&1
    fi
  fi
}

# Make last bootloader label (installer, live, run etc.) as the new default
update_bootmenu() {
  local crnt_default

  if [ -n "$SYSLINUX_DEFAULT" -a -f /flash/syslinux.cfg ]; then
    if grep -q "^LABEL $SYSLINUX_DEFAULT\$" /flash/syslinux.cfg; then
      crnt_default="$(awk '/^DEFAULT/ {print $2}' /flash/syslinux.cfg)"
      if [ ! "$crnt_default" = "$SYSLINUX_DEFAULT" ]; then
        progress "Updating /flash/syslinux.cfg [$crnt_default -> $SYSLINUX_DEFAULT]"

        mount -o remount,rw /flash
        sed -e "s/^SAY Wait for .* mode/SAY Wait for ${SYSLINUX_DEFAULT} mode/" -i /flash/syslinux.cfg
        sed -e "s/^DEFAULT .*/DEFAULT $SYSLINUX_DEFAULT/" -i /flash/syslinux.cfg
        rm -f /flash/EFI/BOOT/syslinux.cfg
        mount -o remount,ro /flash
      fi
    fi
  fi

  if [ -n "$GRUB_DEFAULT" -a -f /flash/EFI/BOOT/grub.cfg ]; then
    if grep -q "^menuentry \"$GRUB_DEFAULT\"" /flash/EFI/BOOT/grub.cfg; then
      crnt_default="$(awk '/^set default/ {print substr($2,9,19)}' /flash/EFI/BOOT/grub.cfg)"
      if [ ! "$crnt_default" = "\"$GRUB_DEFAULT\"" ]; then
        progress "Updating /flash/EFI/BOOT/grub.cfg [$crnt_default -> \"$GRUB_DEFAULT\"]"

        mount -o remount,rw /flash
        sed -e "s/^set default=.*/set default=\"$GRUB_DEFAULT\"/" -i /flash/EFI/BOOT/grub.cfg
        rm -f /flash/grub.cfg
        mount -o remount,ro /flash
      fi
    fi
  fi
}

check_out_of_space() {
  if [ "$(df /storage | awk '/[0-9]%/{print $4}')" -eq "0" ]; then
    echo ""
    echo "The $1 is corrupt, or there is not enough"
    echo "free space on /storage to complete the update!"
    echo ""
    echo "Please free up space on your /storage partition"
    echo "by deleting unecessary files, then try again."
    echo ""
    return 0
  else
    echo ""
    echo "The $1 is corrupt/invalid!"
    echo ""
    return 1
  fi
}

do_cleanup() {
  StartProgress spinner "Cleaning up... "

  if mountpoint -q /storage/roms; then
    umount /storage/roms &>/dev/null
  fi

  if mountpoint -q /storage; then
    umount /storage &>/dev/null
  fi

  if mountpoint -q /update; then
    umount /update &>/dev/null
  fi

  if [ -d $UPDATE_ROOT/.tmp/mnt ]; then
    if mountpoint -q $UPDATE_ROOT/.tmp/mnt ; then
      # busybox umount deletes loop device automatically
      umount $UPDATE_ROOT/.tmp/mnt &>/dev/null
    fi

    [ -n $LOOP ] && losetup -d $LOOP &>/dev/null
  fi

  [ -f "$UPDATE_TAR" ] && rm -f "$UPDATE_TAR" &>/dev/null
  [ -f "$UPDATE_IMG_GZ" ] && rm -f "$UPDATE_IMG_GZ" &>/dev/null
  [ -f "$UPDATE_IMG" ] && rm -f "$UPDATE_IMG" &>/dev/null

  rm -rf $UPDATE_ROOT/[0-9a-zA-Z]* &>/dev/null
  rm -f  $UPDATE_ROOT/* &>/dev/null
  rm -f  $UPDATE_ROOT/.nocheck $UPDATE_ROOT/.nocompat &>/dev/null
  rm -rf $UPDATE_ROOT/.tmp &>/dev/null

  sync

  StopProgress "done"
}

check_update() {
  progress "Checking for updates"
  UPDATE_TAR=$(ls -1 "$UPDATE_DIR"/*.tar 2>/dev/null | head -n 1)
  UPDATE_IMG_GZ=$(ls -1 "$UPDATE_DIR"/*.img.gz 2>/dev/null | head -n 1)
  UPDATE_IMG=$(ls -1 "$UPDATE_DIR"/*.img 2>/dev/null | head -n 1)

  if ! [ -f "$UPDATE_DIR/$UPDATE_KERNEL" -a -f "$UPDATE_DIR/$UPDATE_SYSTEM" ] &&
     ! [ -f "$UPDATE_TAR" -o -f "$UPDATE_IMG_GZ" -o -f "$UPDATE_IMG" ]; then
    return 0
  fi

  if [ "$UPDATE_DISABLED" = "yes" ]; then
    echo "Updating is not supported on netboot"
    do_cleanup
    StartProgress countdown "Normal startup in 5s... " 5 "NOW"
    return 0
  fi

  if [ -d $UPDATE_DIR/.tmp ]; then
    # This isn't really a failed update, it's just a failure to clean up after updating.
    #echo "Failed update detected - performing recovery."
    #echo ""
    do_cleanup
    StartProgress countdown "Reboot in 5... " 5 "NOW"
    sync
    reboot
    #return 0
  fi

  mkdir -p $UPDATE_DIR/.tmp &>/dev/null
  sync

  clear >/dev/console
  echo "UPDATE IN PROGRESS"
  echo ""
  echo "Please do not reboot or turn off your @DISTRONAME@ device!"
  echo ""

  if [ -f "$UPDATE_TAR" ]; then
    TARRESULT="0"

    echo "Found new .tar archive"
    UPDATE_FILENAME="$UPDATE_TAR"
    StartProgress spinner "Extracting contents of archive... "
      tar -xf "$UPDATE_TAR" -C $UPDATE_DIR/.tmp 1>/dev/null 2>/tmp/tarresult.txt || TARRESULT="1"

    if [ "${TARRESULT}" -eq "0" ]; then
      mv $UPDATE_DIR/.tmp/*/target/* $UPDATE_DIR &>/dev/null
      sync
      StopProgress "done"
    else
      StopProgress "FAILED"

      echo "Failed to extract contents of archive file!"
      echo "tar result: '$(cat /tmp/tarresult.txt)'"

      check_out_of_space "archive"

      do_cleanup
      StartProgress countdown "Normal startup in 30s... " 30 "NOW"
      return 0
    fi
  elif [ -f "$UPDATE_IMG_GZ" -o -f "$UPDATE_IMG" ]; then
    mkdir -p $UPDATE_DIR/.tmp/mnt &>/dev/null
    IMG_FILE="$UPDATE_DIR/.tmp/update.img"
    GZRESULT="0"

    if [ -f "$UPDATE_IMG_GZ" ]; then
      echo "Found new compressed image file"
      UPDATE_FILENAME="$UPDATE_IMG_GZ"
      StartProgress spinner "Decompressing image file... "
        gunzip -d -c "$UPDATE_IMG_GZ" 1>$IMG_FILE 2>/tmp/gzresult.txt || GZRESULT="1"
        sync
        [ "${GZRESULT}" -eq "0" ] && StopProgress "OK" || StopProgress "FAILED"

      if [ "${GZRESULT}" -eq "1" ]; then
        echo "Failed to decompress image file!"
        echo "gunzip result: '$(cat /tmp/gzresult.txt)'"

        check_out_of_space "compressed image"

        do_cleanup
        StartProgress countdown "Normal startup in 30s... " 30 "NOW"
        return 0
      fi
    else
      echo "Found new image file"
      UPDATE_FILENAME="$UPDATE_IMG"
      mv "$UPDATE_IMG" $IMG_FILE
    fi

    LOOP=$(losetup -f)
    LOOP_NUM=$(echo $LOOP | sed 's|/dev/loop||')
    mknod $LOOP b 7 $LOOP_NUM &>/dev/null
    losetup $LOOP $IMG_FILE

    # check for MBR partititon
    OFFSET=$(fdisk -u -l $LOOP 2>/dev/null | awk '/^[ ]*Device/{part=1; next}; part{if ($2 == "*") {print $5} else {print $4} ; exit}')
    if [ -z "$OFFSET" ]; then
      # check for GPT partititon
      OFFSET=$(fdisk -u -l $LOOP 2>/dev/null | awk '/^Number/{part=1; next}; part{print $2; exit}')
      if [ -z "$OFFSET" ]; then
        echo "Could not find a valid system partition in image file!"
        do_cleanup
        StartProgress countdown "Normal startup in 5s... " 5 "NOW"
        return 0
      fi
    fi

    SECTOR_SIZE=$(cat /sys/devices/virtual/block/loop${LOOP_NUM}/queue/hw_sector_size)
    losetup -d $LOOP
    sync

    OFFSET=$(($OFFSET * $SECTOR_SIZE))

    # use losetup because busybox mount does not support the -o offset option
    echo "Mounting system partition..."
    losetup -o $OFFSET $LOOP $IMG_FILE
    mount -o ro,loop $LOOP $UPDATE_DIR/.tmp/mnt

    # don't make temporary files but instead copy
    # directly from mountpoint to /flash
    UPDATE_DIR=$UPDATE_ROOT/.tmp/mnt
    UPDATE_KERNEL="@KERNEL_NAME@"
  else
    UPDATE_FILENAME="$UPDATE_DIR/$UPDATE_SYSTEM"
  fi

  sync

  if [ ! -b "/$IMAGE_KERNEL" -a ! -f "/flash/$IMAGE_KERNEL" ] || [ ! -f "/flash/$IMAGE_SYSTEM" ]; then
    echo "Missing (target) ${IMAGE_KERNEL} or ${IMAGE_SYSTEM}!"
    do_cleanup
    StartProgress countdown "Normal startup in 30s... " 30 "NOW"
    return 0
  fi

  if [ ! -f "$UPDATE_DIR/$UPDATE_KERNEL" -o ! -f "$UPDATE_DIR/$UPDATE_SYSTEM" ]; then
    echo "Missing (source) ${UPDATE_KERNEL} or ${UPDATE_SYSTEM}!"
    do_cleanup
    StartProgress countdown "Normal startup in 30s... " 30 "NOW"
    return 0
  fi

  # check md5 sums if .nocheck doesn't exist
  if [ ! -f "$UPDATE_ROOT/.nocheck" ]; then
    if [ -f "$UPDATE_DIR/${UPDATE_KERNEL}.md5" -a -f "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" ]; then
      # *.md5 size-check
      if [ ! -s "$UPDATE_DIR/${UPDATE_KERNEL}.md5" -o ! -s "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" ]; then
        echo "Zero-sized .md5 file!"
        MD5_FAILED="1"
      else
        sed "s#target/KERNEL#$UPDATE_DIR/$UPDATE_KERNEL#g" "$UPDATE_DIR/${UPDATE_KERNEL}.md5" >"$UPDATE_ROOT/${UPDATE_KERNEL}.check.md5"
        sed "s#target#$UPDATE_DIR#g" "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" >"$UPDATE_ROOT/${UPDATE_SYSTEM}.check.md5"

        StartProgress spinner "Checking ${UPDATE_KERNEL}.md5... "
          if md5sum -sc "$UPDATE_ROOT/${UPDATE_KERNEL}.check.md5"; then
            StopProgress "OK"
          else
            StopProgress "FAILED"
            MD5_FAILED="1"
          fi

        StartProgress spinner "Checking ${UPDATE_SYSTEM}.md5... "
          if md5sum -sc "$UPDATE_ROOT/${UPDATE_SYSTEM}.check.md5"; then
            StopProgress "OK"
          else
            StopProgress "FAILED"
            MD5_FAILED="1"
          fi
      fi
    else
      echo "Missing ${UPDATE_KERNEL}.md5 or ${UPDATE_SYSTEM}.md5!"
      MD5_FAILED="1"
    fi

    if [ "$MD5_FAILED" -eq "1" ]; then
      echo "md5 check failed!"
      do_cleanup
      StartProgress countdown "Normal startup in 30s... " 30 "NOW"
      return 0
    fi
  fi

  mount_part "$UPDATE_DIR/$UPDATE_SYSTEM" "/update" "ro,loop"

  # Verify that the new update is compatible with the current system - this should avoid creating
  # non-booting systems after (for example) an RPi tar is incorrectly applied to an RPi2 system.
  if [ ! -f "$UPDATE_ROOT/.nocompat" ]; then
    if ! check_is_compatible "$UPDATE_FILENAME"; then
      do_cleanup
      StartProgress countdown "Normal startup in 60s... " 60 "NOW"
      return 0
    fi
  fi

  # get sizes
  FLASH_FREE=$(df /flash/ | awk '/[0-9]%/{print $4}')
  FLASH_FREE=$(( $FLASH_FREE * 1024 ))

  # Disregard kernel size if it's a a block device
  if [ ! -b "/$IMAGE_KERNEL" ]; then
    OLD_KERNEL=$(stat -t "/flash/$IMAGE_KERNEL" | awk '{print $2}')
  else
    OLD_KERNEL="0"
  fi

  OLD_SYSTEM=$(stat -t "/flash/$IMAGE_SYSTEM" | awk '{print $2}')
  NEW_KERNEL=$(stat -t "$UPDATE_DIR/$UPDATE_KERNEL" | awk '{print $2}')
  NEW_SYSTEM=$(stat -t "$UPDATE_DIR/$UPDATE_SYSTEM" | awk '{print $2}')

  # old KERNEL+SYSTEM+free space - new KERNEL+SYSTEM must be higher than 5MB
  # at least 5MB free after update

  TMP_SIZE=$((OLD_KERNEL + OLD_SYSTEM + FLASH_FREE - NEW_KERNEL - NEW_SYSTEM))
  FLASH_FREE_MIN=$((FLASH_FREE_MIN * 1024 * 1024))

  if [ $TMP_SIZE -ge $FLASH_FREE_MIN ]; then
    echo "Checking size: OK"
  else
    echo "Checking size: FAILED"
    echo ""
    echo "Your System (FAT) partition is too small for this update,"
    echo "and there is not enough space for the update to be installed!"
    echo ""
    echo "You must re-install your system using the disk image of a"
    echo "current release."
    echo ""
    do_cleanup
    StartProgress countdown "Normal startup in 60s... " 60 "NOW"
    return 0
  fi

  # all ok, update
  display_versions
  if [ -b "/$IMAGE_KERNEL" ]; then
    update_partition "Kernel" "$UPDATE_KERNEL" "/$IMAGE_KERNEL"
  else
    update_file "Kernel" "$UPDATE_KERNEL" "/flash/$IMAGE_KERNEL"
  fi
  umount /sysroot &>/dev/null
  update_file "System" "$UPDATE_SYSTEM" "/flash/$IMAGE_SYSTEM"
  update_bootloader
  sync
  StartProgress countdown "Update complete. Reboot in 5s... " 5 "NOW"
  do_cleanup
  sync
  do_reboot
}

prepare_sysroot() {
  progress "Preparing system"

  mount --move /flash /sysroot/flash
  mount --move /storage /sysroot/storage

  if [ ! -d "/sysroot/usr/lib/kernel-overlays/base/lib/modules/$(uname -r)/" -a -f "/sysroot/usr/lib/systemd/systemd" ]; then
    echo ""
    echo "NEVER TOUCH boot= in syslinux.conf / cmdline.txt!"
    echo "If you don't know what you are doing,"
    echo "your installation is now broken."
    echo ""
    StartProgress countdown "Normal startup in 60s... " 60 "NOW"
  fi

  [ -f "/sysroot/usr/lib/systemd/systemd" ] || error "final_check" "Could not find systemd!"
}

check_amlogic_dtb() {
  if grep -q "amlogic" /proc/device-tree/compatible; then
    if grep -q "official" /sysroot/etc/os-release; then
      progress "Checking Amlogic DTB"
      if [ "$(uname -r)" = "3.14.29" ]; then
        DT_ID=$(cat /proc/device-tree/le-dt-id 2>/dev/null)
      else
        DT_ID=$(cat /proc/device-tree/coreelec-dt-id 2>/dev/null)
      fi
      DT_FILE=$(ls -1 /sysroot/usr/share/bootloader/device_trees/${DT_ID}.dtb 2>/dev/null | head -n 1)

      if [ -f "/proc/device-tree/coreelec" ] &&
         [ -n "${DT_ID}" ] &&
         [ -f "${DT_FILE}" ]; then
         return 0
      fi

      echo "WARNING: Your device-tree is out-of-date!"
      echo ""
      echo "Please update it to resume normal startup."
      echo ""

      StartProgress countdown "Normal startup in 30s... " 30 "NOW"
      echo ""
    fi
  fi
}

# create pipe and save original descriptors
create_output_pipe() {
  # save original stdout and stderr descriptors
  exec 4>&1
  exec 5>&2

  # create pipe and use it with tee
  mknod /tmp/output_pipe p
  tee </tmp/output_pipe /dev/init.log &
  TEE_PID=$!
}

# redirect stdout and stderr
redirect_output_to_pipe() {
  exec 1>/tmp/output_pipe
  exec 2>/tmp/output_pipe
}

# restore original descriptors
redirect_output_to_screen() {
  exec 1>&4
  exec 2>&5
}

# delete descriptor
delete_descriptors() {
  exec 4>&-
  exec 5>&-
}

# Do init tasks to bring up system

# set ondemand up_threshold
if [ -e /sys/devices/system/cpu/cpufreq/ondemand/up_threshold ]; then
  echo 50 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold
else
  for f in $(ls /sys/devices/system/cpu/cpufreq/policy*/ondemand/up_threshold 2>/dev/null) ; do
    echo 50 > $f
  done
fi

# run platform_init script if exists
if [ -f "./platform_init" ]; then
  ./platform_init
fi

# clear screen and hide cursor
clear
hidecursor

create_output_pipe
redirect_output_to_pipe

# parse command line arguments
for arg in $(cat /proc/cmdline); do
  case $arg in
    BOOT_IMAGE=*)
      IMAGE_KERNEL="${arg#*=}"
      [ "${IMAGE_KERNEL:0:1}" = "/" ] && IMAGE_KERNEL="${IMAGE_KERNEL:1}"
      ;;
    SYSTEM_IMAGE=*)
      IMAGE_SYSTEM="${arg#*=}"
      [ "${IMAGE_SYSTEM:0:1}" = "/" ] && IMAGE_SYSTEM="${IMAGE_SYSTEM:1}"
      ;;
    boot=*)
      boot="${arg#*=}"
      case $boot in
        ISCSI=*|NBD=*|NFS=*)
          UPDATE_DISABLED=yes
          FLASH_NETBOOT=yes
          ;;
        /dev/*|LABEL=*|UUID=*)
          RUN_FSCK_DISKS="$RUN_FSCK_DISKS $boot"
          ;;
        FOLDER=*)
          RUN_FSCK_DISKS="$RUN_FSCK_DISKS ${boot#*=}"
          ;;
      esac
      ;;
    disk=*)
      disk="${arg#*=}"
      case $disk in
        ISCSI=*|NBD=*|NFS=*)
          STORAGE_NETBOOT=yes
          ;;
        /dev/*|LABEL=*|UUID=*)
          RUN_FSCK_DISKS="$RUN_FSCK_DISKS $disk"
          ;;
        FOLDER=*)
          RUN_FSCK_DISKS="$RUN_FSCK_DISKS ${disk#*=}"
          ;;
      esac
      ;;
    wol_mac=*)
      wol_mac="${arg#*=}"
      ;;
    wol_wait=*)
      wol_wait="${arg#*=}"
      ;;
    textmode)
      INIT_UNIT="--unit=textmode.target"
      ;;
    installer)
      INIT_UNIT="--unit=installer.target"
      SYSLINUX_DEFAULT="installer"
      ;;
    debugging)
      DEBUG=yes
      ;;
    nopkmute)
      MUTE_PRINTK=no
      ;;
    progress)
      PROGRESS=yes
      INIT_ARGS="$INIT_ARGS --show-status=1"
      ;;
    nofsck)
      RUN_FSCK=no
      ;;
    nosplash)
      SPLASH=no
      ;;
    toram)
      SYSTEM_TORAM=yes
      ;;
    live)
      LIVE=yes
      SYSLINUX_DEFAULT="live"
      ;;
    portable)
      SYSLINUX_DEFAULT="run"
      ;;
    grub_live)
      LIVE=yes
      GRUB_DEFAULT="Live"
      ;;
    grub_portable)
      GRUB_DEFAULT="Run"
      ;;
    overlay)
      OVERLAY=yes
      ;;
    setfbres=*)
      SWITCH_FRAMEBUFFER="${arg#*=}"
      SWITCH_FRAMEBUFFER="${SWITCH_FRAMEBUFFER//,/ }"
      ;;
    break=*)
      BREAK="${arg#*=}"
      ;;
    bigfont=*)
      BIGFONT="${arg#*=}"
      ;;
    ip=*)
      KERNEL_IPCONFIG="yes"
      ;;
  esac
done

# hide kernel log messages on console
if [ ! "$MUTE_PRINTK" = "no" ]; then
  echo '1 4 1 7' > /proc/sys/kernel/printk
fi

if test "$DEBUG" = "yes"; then
  exec 3>&1
else
  exec 3>/dev/null
fi
SILENT_OUT=3

# If the network is up (due to the use of the "ip" kernel parameter) and a DNS
# server is known, allow the libc resolver to use it
#grep '^\(nameserver\|domain\) ' /proc/net/pnp | grep -v '^nameserver 0\.0\.0\.0$' > /etc/resolv.conf

if [ "${boot%%=*}" = "FILE" ]; then
  error "check arguments" "boot argument can't be FILE type..."
fi

debug_msg "Unique identifier for this client: ${MACHINE_UID:-NOT AVAILABLE}"

# main boot sequence
for BOOT_STEP in \
    load_modules \
    check_disks \
    mount_flash \
    set_consolefont \
    cleanup_flash \
    update_bootmenu \
    mount_sysroot \
    mount_storage \
    load_splash \
    mount_games \
    check_update \
    prepare_sysroot
do
  $BOOT_STEP
  [ -n "$DEBUG" ] && break_after $BOOT_STEP
done

BOOT_STEP=final

# log if booting from usb / removable storage
STORAGE=$(cat /proc/mounts | grep " /sysroot/storage " | awk '{print $1}' | awk -F '/' '{print $3}')
FLASH=$(cat /proc/mounts | grep " /sysroot/flash " | awk '{print $1}' | awk -F '/' '{print $3}')
for i in $STORAGE $FLASH ; do
  if [ -n "$i" ]; then
    removable="/sys/class/block/*/$i/../removable"
    if [ -e $removable ]; then
      if [ "$(cat $removable 2>/dev/null)" = "1" ]; then
        echo "### BIG FAT WARNING" > /dev/kmsg
        echo "### $i is removable. suspend/resume may not work" > /dev/kmsg
      fi
    fi
  fi
done
# move some special filesystems
/usr/bin/busybox mount --move /dev /sysroot/dev
/usr/bin/busybox mount --move /proc /sysroot/proc
/usr/bin/busybox mount --move /sys /sysroot/sys
/usr/bin/busybox rm -fr /tmp

# tell OE settings addon to disable updates
if [ "$UPDATE_DISABLED" = "yes" ]; then
  echo "" > /sysroot/dev/.update_disabled
fi

if [ "$FLASH_NETBOOT" = "yes" ]; then
  echo "" > /sysroot/dev/.flash_netboot
fi

if [ "$KERNEL_IPCONFIG" = "yes" ]; then
  echo "" > /sysroot/dev/.kernel_ipconfig
fi

# swap can not be used over nfs.(see scripts/mount-swap)
if [ "$STORAGE_NETBOOT" = "yes" ]; then
  echo "" > /sysroot/dev/.storage_netboot
fi

BACKUP_FILE=$(ls -1 /sysroot/storage/.restore/??????????????.tar 2>/dev/null | head -n 1)

if [ -f /sysroot/storage/.please_resize_me ]; then
  INIT_UNIT="--unit=fs-resize.target"
elif [ -f /sysroot/storage/.cache/reset_oe -o -f /sysroot/storage/.cache/reset_xbmc ]; then
  INIT_UNIT="--unit=factory-reset.target"
elif [ -f "$BACKUP_FILE" ]; then
  INIT_UNIT="--unit=backup-restore.target"
elif [ -f /sysroot/storage/.rpi_flash_firmware ]; then
  INIT_UNIT="--unit=rpi-flash-firmware.target"
fi

# stop output redirection
[ -n "$TEE_PID" ] && kill $TEE_PID &>/dev/null
if [ -s /sysroot/dev/init.log ]; then
  mv /sysroot/dev/init.log /sysroot/storage/init.log
else
  rm -f /sysroot/dev/init.log
  rm -f /sysroot/storage/init.log
fi

# restore original descriptors and delete descriptors
redirect_output_to_screen
delete_descriptors

# switch to new sysroot and start real init
exec /usr/bin/busybox switch_root /sysroot /usr/lib/systemd/systemd $INIT_ARGS $INIT_UNIT

error "switch_root" "Error in initramfs. Could not switch to new root"
